题目为什么叫setTimeout的第一个参数而不是回调函数?如果你心中有稍有疑惑,或许应该看看下面的文章

我们日常使用setTimeout(),一般是将函数作为第一个参数,但是也有例外情况,先看以下代码:

function test() {

  var cl = function() {

    console.log(666)

  }

  setTimeout('cl()', 1500)

}

test()

将以上代码CV到chrome中的console,运行发现:

Uncaught ReferenceError: cl is not defined

没有定义cl函数,奇怪是并没有报Uncaught SyntaxError: Unexpected identifier这样的语法错误,查javascript MDN我们就会发现:

setTimeout允许讲一个字符串作为第一个参数,而且js内部将会调用eval()函数用来动态执行一段字符串脚本,至于为什么找不到cl函数,我们猜想是作用域问题,既然是eval动态执行,我们在字符串参数中输出当前this绑定的对象:

function test() {

  var cl = function() {

    console.log(666)

  }

  setTimeout('console.log(this);cl()', 1500)

}

test()

执行后发现:

原来this绑定window全局对象,这下明白了,eval()执行动态脚本的时候,在全局作用域并没有找到我们定义在函数test内部的cl,所以会报错。

我们将cl定义移到外部:

ok了

————分割线————

var cl = function() {console.log(666)}

setTimeout(cl(), 1500)

经常看到有新人在社区上问这段代码为什么没有延迟执行,只需注意这边的cl()是一个函数执行而不是函数定义,如果想延迟执行,我们需要传递一个函数地址,比如:

var cl = function() {console.log(666)}

setTimeout(cl, 1500)

或者直接return一个函数:

var cl = function() {

    return function() {

        console.log(666)

    }

}

setTimeout(cl(), 1500)

又或者参数处直接定义:

setTimeout(function() {console.log(666)}, 1500)

归根结底还是搞清引用函数地址和执行函数的区别

————分割线————

以上规则也同样适用于字符串参数,只是字符串参数中要加上()保证eval的时候执行,不要到时候只是eval了一个地址 =。=,比如这样:

var cl = function() {

    console.log(666)

  }

function test() {

  setTimeout('cl', 1500)

}

test()

执行一下,啥屁也没看到 = 。=

总之,在setTimeout的时候尽量不要用字符串的参数,因为eval()具有许多不可预见的危险性,比如说可能有意外的运行结果,可能隐式创建全局变量,闭包作用域解析过多消耗,xss,运行慢啊巴拉巴拉之类的。但是我们也需要了解下js的一些黑魔法,以防到时候懵逼。


BenjaminShih
260 声望6 粉丝